/*
*****************************************************
*                                                   *
* nokia 3310 LCD scope                              *
* THIS IS IT!!! :-) june 1 , 2009                   *
*
* B0 - uS/Div switch
* B1 - Y pos
* B2 - sw soft GAIN
* B4 - spst sw HOLD function /hold MODE open sw
* B5 - trigger LVL button / 5 & 128

 WHATS NEW/UPGRADED:
 ===================
 shows a PROPER square wave
 has switchable GAIN FS=5V and FS=2.6V
 has also TRIGGER/hold function by:
 master Regulus Berdin A.K.A zer0w1ng
 
 much accurate;  Tosc = 32 @ 20MHz bus clock
 
 100ms 50mS 20mS 10mS 2mS 500uS us/Div
 
 -ADD trigger TOGGLE function (done!)
 -ADD better HOLD function (done!)
 -ADD toggle function to softGAIN (done!)
*****************************************************

Changes:
-long press 
*/



#include <18F452.h>
#device adc=8


#use delay(clock=20M)
//#FUSES NOWDT,NOPROTECT,NOOSCSEN,BROWNOUT,BORV20,STVREN,NODEBUG,NOWRT,NOWRTD,NOWRTB
//#FUSES NOWRTC,NOCPD,NOCPB,NOEBTR,NOEBTRB
#FUSES NOWDT, HS, NOPUT, NOPROTECT, NOBROWNOUT, NOLVP, NOCPD, NOWRT, NODEBUG
#use fast_io(b)

#define LCD_SCLK    4
#define LCD_SDA     2 
#define LCD_DC      3
#define LCD_CS      1
#define LCD_RES     0

#byte lcdport         = 0xF83   
#byte lcdport_tris   = 0xF95 
   
   #bit nok_sclk = lcdport.LCD_SCLK     //pin2 LCD  port_D4
   #bit nok_sda  = lcdport.LCD_SDA      //pin3 LCD  port_D2
   #bit nok_dc   = lcdport.LCD_DC       //pin4 LCD  port_D3
   #bit nok_cs   = lcdport.LCD_CS       //pin5 LCD  port_D1
   #bit nok_res  = lcdport.LCD_RES      //pin8 LCD  port_D0
   //============================================================

//#include <2465.C>
#include "3310.c"

#define  speaker                 pin_C7

#define TIMEOUT                  1000
#define DEFAULT_TRIGGER_DELAY    1
#define BTN_MASK                 (1<<5 | 1<<4 | 1<<3 | 1<<2 | 1<<1 | 1)
#define DEBOUNCE_MS              30
#define MINIMUM_ADC_TIME         15

#define TRIGGER_LED_PORT    PIN_D7

/* 100ms, 50ms, 20ms, 10ms, 2ms, 500uS */
#define INTERVAL_COUNT      6
#define INTERVAL_DEFAULT    2
#define INTERVAL_STR_SIZE   6

#define SCALE_COUNT         4
#define SCALE_DEFAULT       0
#define SCALE_STR_SIZE      6

#define TRIGGER_DEFAULT     16

unsigned char samples[84];
unsigned int8 i;

unsigned int scale;
unsigned int8 trigger_level = TRIGGER_DEFAULT;
signed int8 position = 0;

unsigned int8 interval = INTERVAL_DEFAULT;
unsigned int8 scale_index = SCALE_DEFAULT;
unsigned int8 scale_dc_level = (128 >> (3-SCALE_DEFAULT));

unsigned int16 timer_value;
unsigned int8  timer_divider;

typedef struct {
    unsigned int16 value;
    char *str;
} val_str_t;

const unsigned int16 const interval_delay[INTERVAL_COUNT] = {
    1000, 500, 200, 100, 20, 5
};


const unsigned int8 const scale_value[SCALE_COUNT] = {8,4,2,1};


void view_settings();
void beep();
void plot_samples(void);
signed int8 compute_y(unsigned int8 v);



void scope_settings()
{
   
   
   delay_ms(50); //switch debounce
   
   nokia_clean_ddram();
   view_settings();
}


//------------------------


void show_scale(void)
{
    nokia_gotoxy(25,5);
    switch (scale)
    {
        case 8:
            printf(nokia_printchar, " 5V "); 
            break;
        case 4:
            printf(nokia_printchar, " 2V5"); 
            break;
        case 2:
            printf(nokia_printchar, "1V25"); 
            break;
        case 1:
            printf(nokia_printchar, "V625"); 
            break;
    }
}

void show_interval(void)
{
    unsigned int8 interv;
    
    interv = interval_delay[interval]/10;
    
    nokia_gotoxy(53,5);
    if (interv)
        printf(nokia_printchar, "%3ums", interv);
    else
        printf(nokia_printchar, "500us");
}


void erase_trigger(void)
{
    nokia_gotoxy(0, (trigger_level/8));
    nokia_write_data(0x00);
    nokia_gotoxy(1, (trigger_level/8));
    nokia_write_data(0x00);
    nokia_gotoxy(0,5);
    printf(nokia_printchar,"   ");
}

void show_trigger(void)
{
    LcdPixel(0, trigger_level);
    LcdPixel(1, trigger_level);
    nokia_gotoxy(0,5); 
    printf(nokia_printchar,"t%u", 31-trigger_level);
}

void clear_status_line(void)
{
   nokia_gotoxy(0,5);
   printf(nokia_printchar,"              ");
}

void view_settings()     
{
   clear_status_line();

   show_interval();
   show_scale();
   show_trigger();
}




enum {
    STATE_FIND_LOW,
    STATE_FIND_TRIGGER,
    STATE_COUNTING_LOW,
    STATE_COUNTING_HIGH,
    STATE_DUMMY
};
void show_frequency()
{
    unsigned int16 cnt;
    unsigned int8 i, state;
    float d, intv;
    signed int8 y, tr;

    state = STATE_COUNTING_LOW;
    cnt = 0;
    
    tr = compute_y(samples[0]);
    
    for (i=0;i<84;++i)
    {
        y = compute_y(samples[i]);
        switch (state)
        {
            case STATE_COUNTING_LOW:
                ++cnt;
                if (y>tr)
                    state = STATE_COUNTING_HIGH;
                break;
            case STATE_COUNTING_HIGH:
                ++cnt;
                if (y<=tr)
                    i=100;
                break;
        }
    }
    
#if 0
    printf(nokia_printchar,"%Lu :%Lu %Lu", cnt, timer_value,timer_divider);
#else
    //timer_divider
    if (i>=100)
    {
        d = timer_value/84.0;
        intv = cnt;
        intv *= timer_divider;
        printf(nokia_printchar,"Freq:%2.3gkHz", 5000.0/(intv * d));
    }
    else
    {
        printf(nokia_printchar,"Freq: invalid");
    }
#endif
}


//===================================================================


signed int8 compute_y(unsigned int8 v)
{
#if 1
    return position + 15 - ((v >> (3-scale_index)) - scale_dc_level) ;
#else
   v >>= (3-scale_index); 
   return position + 31 - v;
#endif   
}


unsigned char debounce_pin(unsigned char bmask)
{
    if ((input_b() & bmask))
       return 0;
      
    delay_ms(DEBOUNCE_MS);
   
    if ((input_b() & bmask))
       return 0;
       
    return 1;
}


unsigned int8 wait_for_release(void)
{
    if ((input_b() & BTN_MASK) != BTN_MASK)
        return 1;
    
    delay_ms(DEBOUNCE_MS);
    
    if ((input_b() & BTN_MASK) != BTN_MASK)
        return 1;
    
    return 0;
}



void adjust_trigger(void)
{
    erase_trigger();
    if (trigger_level>1)
        --trigger_level;
    else
        trigger_level = 30;
    show_trigger();
}


void adjust_position(void)
{
    --position;
    if (position<-40)
        position = 40;
    plot_samples();        
    
    nokia_gotoxy(60,5);
    printf(nokia_printchar,"   ");
    nokia_gotoxy(60,5);
    printf(nokia_printchar,"%d", -position);
    
}




void adjust_interval(void)
{
    ++interval;
    if (interval>=INTERVAL_COUNT)
        interval = 0;
        
    show_interval();
}


void check_buttons(void)
{
    unsigned int8 tmr;

    //check for any buttons pressed
    if ((input_b() & BTN_MASK) == BTN_MASK)
        return;
   
    //position
    if (debounce_pin(1<<1))
    {
        nokia_gotoxy(0,5);
        printf(nokia_printchar,"Position:     ");
    
        adjust_position();
        tmr = 34;   /* 500ms long press*/
        while(wait_for_release())
        {
            delay_ms(15);
            if (tmr)
                --tmr;
            else
                adjust_position();
        }
        view_settings();
    }
   
    //trigger level
    if (debounce_pin(1<<5))
    {
        adjust_trigger();
        tmr = 10;           /* 500ms long press*/
        while(wait_for_release())
        {
            delay_ms(50);
            if (tmr)
                --tmr;
            else
                adjust_trigger();
        }
    }

    //Gain/scale
    if (debounce_pin(1<<2))
    {
        ++scale_index;
        if (scale_index >= SCALE_COUNT)
            scale_index = 0;
            
        scale = scale_value[scale_index];
        scale_dc_level = (128 >> (3-scale_index));
        
        show_scale();
        
        while(wait_for_release())
            continue;
    }

    //Hold
    if (debounce_pin(1<<4))
    {
        nokia_gotoxy(0,5); 
        printf(nokia_printchar,"H  ");
    
        while(wait_for_release())
            continue;
            
        view_settings();
    }

    //interval
    if (debounce_pin(1))
    {
        adjust_interval();
        while(wait_for_release())
            continue;
    }
    
    
    //freq
    if (debounce_pin(1<<3))
    {
        clear_status_line();
        nokia_gotoxy(0,5); 
        
        show_frequency();
        
        while(wait_for_release())
            continue;
            
        view_settings();
            
    }
    
}


void trigger(void)
{
   unsigned int16 timeout1, timeout2;

   output_high(TRIGGER_LED_PORT);

    //wait until reading gets below mid
    timeout1 = TIMEOUT;
    do {
        check_buttons();      
        read_adc(adc_start_only);
        delay_us(MINIMUM_ADC_TIME + 30);
        if (compute_y(read_adc(adc_read_only))>=trigger_level) 
            break;
        
    } while (--timeout1);
    
    //wait until reading gets above mid
    timeout2 = TIMEOUT;
    do   
    {
        check_buttons();      
        read_adc(adc_start_only);
        delay_us(MINIMUM_ADC_TIME + 30);
        if (compute_y(read_adc(adc_read_only))<trigger_level) 
            break;
    } while (--timeout2);


    nokia_gotoxy(0,5); 
    if (timeout1 == 0 || timeout2 == 0)
    {
      output_low(TRIGGER_LED_PORT);
     
    }
    else 
    {
     
    }
}


#define PLOT_START  3
#define PLOT_END    83

void plot_samples(void)
{
    signed int8 y1, y2, yz;
    unsigned int8 x;
    
    //6 x 84

    erase_column(PLOT_START);
    yz = compute_y(samples[0]);
    
    for(x=PLOT_START+1; x<=PLOT_END; x++)
    {
        y1 = yz;
        yz = y2 = compute_y(samples[x-PLOT_START]);
        erase_column(x);
 
        if ( (y1<0 && y2<0) || (y1>31 && y2>31) 
            /* || (y1>31 && y2<0) || (y2>31 && y1<0) */  )
            continue;
 
        if (y1>31) y1 = 31;
        if (y2>31) y2 = 31;
        if (y1<0) y1 = 0;
        if (y2<0) y2 = 0;
        lcd_fast_vertical_line(x,y1,y2);
    }
}



void get_samples(void)
{
    unsigned int8 i;
    unsigned int16 pinterval;

    pinterval = interval_delay[interval];
    
    if (pinterval<MINIMUM_ADC_TIME)
        pinterval = MINIMUM_ADC_TIME;

    // 0     1    2   3    4   5
    //1000, 500, 200, 100, 20, 5
    switch(interval)
    {
    case 0:
        setup_timer_1( T1_INTERNAL | T1_DIV_BY_8 );
        timer_divider = 8;
        break;
    case 1:
        setup_timer_1( T1_INTERNAL | T1_DIV_BY_4 );
        timer_divider = 4;
        break;
    case 2:
        setup_timer_1( T1_INTERNAL | T1_DIV_BY_2 );
        timer_divider = 2;
        break;
    default:
        setup_timer_1( T1_INTERNAL | T1_DIV_BY_1 );
        timer_divider = 1;
    }

    set_timer1(0);
    for (i=0;i<84;++i)
    {
        read_adc(adc_start_only);
        delay_us(pinterval);
        samples[i]=read_adc(adc_read_only);
        /* delay_us(2); */
    }
    timer_value = get_timer1();
}



void main()
{
    lcdport_tris = 0x00;                
    
    port_b_pullups (true);
    set_tris_b(BTN_MASK);
   
    nokia_init();
        
    interval = INTERVAL_DEFAULT;        //initial value 20mS scope settings
    scale = scale_value[scale_index];

    setup_adc_ports(AN0_AN1_AN3);
    setup_adc(ADC_CLOCK_DIV_32);  
    set_adc_channel(0);    
    delay_us(10);
   
    nokia_gotoxy(1,1); printf(nokia_printchar,"   Elab.Ph    ");
    nokia_gotoxy(1,2); printf(nokia_printchar,"  3310 scope! ");
    nokia_gotoxy(1,5); printf(nokia_printchar," Version 1.5  ");
   
    delay_ms(1000);
    nokia_clean_ddram();
           
    view_settings();       
    delay_ms(100);
   
    while(true)
    {
        
        trigger();
        get_samples();
        plot_samples();
    }
}

